package com.letoken.platform.pub.annotation.sms;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.enums.ApiResponseEnum;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.pub.res.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Order(-100)
@Component
public class VerificationAspect {




    @Autowired
    StringRedisTemplate redisService;




    @Pointcut("@annotation(com.letoken.platform.pub.annotation.sms.Verification)")
    public void method() {
    }

    @Around(value = "method() && @annotation(valueAnnotation)")
    public Object around(ProceedingJoinPoint point, Verification valueAnnotation) throws Throwable {

        VerifyCodeType vct = valueAnnotation.type();
        if (vct == VerifyCodeType.UNKNOWN){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_001);
        }


        Object[] args = point.getArgs();
        JSONObject o = JSON.parseObject(JSON.toJSONString(args[0]));

        /* 中间处理 */
        Boolean isValid = validTransfer(vct,valueAnnotation,o);



        String key = "";
        if (isValid){
            // 账号属性名
            String accountKey =  valueAnnotation.accountKey();
            // 验证码属性名
            String codeKey = valueAnnotation.codeKey();

            if (StringUtils.isEmpty(accountKey)
                    || StringUtils.isEmpty(codeKey)){
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_001);
            }
            String account = o.getString(accountKey);
            String code = o.getString(codeKey);


            key = String.format(RedisKeyConstant.APP_SMS_CODE,vct.getType(),account);
            /* TODO 存放错误次数 */
            String lockKey = String.format(RedisKeyConstant.APP_SMS_CODE_ERROR_COUNT,vct.getName(),account);
            /* 错误次数处理 */
            errorDro(vct, lockKey);

            log.info("账号：{} 客户端验证码 {} , {} 缓存验证码:{}",account,code,key,redisService.opsForValue().get(key));
            if (redisService.hasKey(key)){
                String x = redisService.opsForValue().get(key);
                if (!code.equals(x)){
                    redisService.opsForValue().increment(lockKey);
                    redisService.expire(lockKey, vct.getTimeWindow(), TimeUnit.SECONDS);
                    throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_002);
                }
            }else {
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_003);
            }
        }


        try {
            Object obj = point.proceed(args);
            if (isValid){
                ApiResponse response = (ApiResponse) obj;
                if (ApiResponseEnum.SUCCESS.getCode() == response.getCode()){
                    redisService.delete(key);
                }
            }
            return obj;
        } catch (Throwable throwable) {
            throw throwable;
        }
    }




    private Boolean validTransfer(VerifyCodeType type,Verification valueAnnotation,JSONObject o){

//        if (!smsConfig.getEnable()){
//            return false; // 不验证
//        }
        if (type == VerifyCodeType.MALL_MERCHANT_SETTLE_IN){
            String codeKey = valueAnnotation.codeKey();
            String code = o.getString(codeKey);
            // 预留手机号 没有传短信验证码，不验证
            if (StringUtils.isBlank(code)){
                return false;
            }
        }

        return true;
    }


    /* 超出错误次数处理 */
    private void errorDro(VerifyCodeType type,String lockKey){
        String value = redisService.opsForValue().get(lockKey);
        if (StringUtils.isNotBlank(value)){
            Integer v = Integer.valueOf(value);
            v = v == null ? 0 : v;
            if (v > 5){
                throw new ApiServiceException(String.format(LetokenApiResponseEnum.ERROR_MALL_SMS_004.getDesc(),type.getLimitTime()/60));
            }
        }

    }


}
